/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.radio.lastfm.panel;

import ch.rolandschaer.ascrblr.radio.MetaData;
import ch.rolandschaer.ascrblr.radio.StationUrl;
import java.io.Serializable;
import java.util.Collection;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import javax.swing.SwingWorker;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import org.openide.awt.HtmlBrowser;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.Lookup.Result;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.NbBundle;
import org.openide.util.NbPreferences;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.openide.util.Utilities;
import org.radio.lastfm.service.PlayerService;

/**
 * Top component which displays something.
 */
final class LastFMPanelTopComponent extends TopComponent implements LookupListener {

    private static final Logger logger = Logger.getLogger(LastFMPanelTopComponent.class.getName());
    
    private final Lookup lookup;
    private final Lookup.Result<MetaData> result;
    
    private static LastFMPanelTopComponent instance;
    /** path to the icon used by the component and its open action */
    static final String ICON_PATH = "org/radio/lastfm/icons/favicon.PNG";
    
    static ImageIcon accept = null;
    static ImageIcon reject = null;

    private static final String PREFERRED_ID = "LastFMPanelTopComponent";

    private LastFMPanelTopComponent() {
        initComponents();
        accept = new ImageIcon(getClass().getResource("/org/radio/lastfm/icons/accept.png"), "");
        reject = new ImageIcon(getClass().getResource("/org/radio/lastfm/icons/cross.png"), "");
        
        setName(NbBundle.getMessage(LastFMPanelTopComponent.class, "CTL_LastFMPanelTopComponent"));
        setToolTipText(NbBundle.getMessage(LastFMPanelTopComponent.class, "HINT_LastFMPanelTopComponent"));
        setIcon(Utilities.loadImage(ICON_PATH, true));
        
        // this sets style
        jTPMetaData.setText(getHtmlForMetaData(null));
        // clicking links opens browser
        jTPMetaData.addHyperlinkListener(new HyperlinkListener() {
            
            public void hyperlinkUpdate(HyperlinkEvent evt) {
                if (evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
                    logger.log(Level.FINE, "Showing URL to in browser: " + evt.getURL().toString());
                    HtmlBrowser.URLDisplayer.getDefault().showURL(evt.getURL());
                }
            }
        });
        
        loadUserPreferencies();
        
        //PlayerService.initLookup();
        lookup = PlayerService.getStaticLookup();
        result = lookup.lookup(new Lookup.Template(MetaData.class));
        result.addLookupListener(this);
        updateMetaData(result);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jPanel1 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        jTUserName = new javax.swing.JTextField();
        jLabel2 = new javax.swing.JLabel();
        jPUserPassword = new javax.swing.JPasswordField();
        jBSignIn = new javax.swing.JButton();
        jCRememberMe = new javax.swing.JCheckBox();
        jLSignStatus = new javax.swing.JLabel();
        jComboBox1 = new javax.swing.JComboBox();
        jTSearchValue = new javax.swing.JTextField();
        jButton2 = new javax.swing.JButton();
        jButton3 = new javax.swing.JButton();
        jButton4 = new javax.swing.JButton();
        jLabel3 = new javax.swing.JLabel();
        jScrollPane2 = new javax.swing.JScrollPane();
        jTPMetaData = new javax.swing.JTextPane();

        jPanel1.setBorder(javax.swing.BorderFactory.createEtchedBorder());

        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, "User name");

        org.openide.awt.Mnemonics.setLocalizedText(jLabel2, "Password");

        org.openide.awt.Mnemonics.setLocalizedText(jBSignIn, "Sign in");
        jBSignIn.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jBSignInActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jCRememberMe, "Remember me");
        jCRememberMe.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jCRememberMeActionPerformed(evt);
            }
        });

        jLSignStatus.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);

        org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(jLabel1)
                    .add(jLabel2))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(jTUserName, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 128, Short.MAX_VALUE)
                    .add(jPUserPassword, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 128, Short.MAX_VALUE))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING, false)
                    .add(jPanel1Layout.createSequentialGroup()
                        .add(jBSignIn)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jLSignStatus, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                    .add(jCRememberMe))
                .addContainerGap())
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(jPanel1Layout.createSequentialGroup()
                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                            .add(jLabel1)
                            .add(jTUserName, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                            .add(jLabel2)
                            .add(jPUserPassword, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))
                    .add(jPanel1Layout.createSequentialGroup()
                        .add(jCRememberMe)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                            .add(jBSignIn)
                            .add(jLSignStatus, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 20, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))))
                .addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        jComboBox1.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "artist", "globaltags" }));

        jTSearchValue.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jTSearchValueActionPerformed(evt);
            }
        });

        jButton2.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/radio/lastfm/icons/control_play.png"))); // NOI18N
        org.openide.awt.Mnemonics.setLocalizedText(jButton2, "Play");
        jButton2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton2ActionPerformed(evt);
            }
        });

        jButton3.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/radio/lastfm/icons/control_end.png"))); // NOI18N
        org.openide.awt.Mnemonics.setLocalizedText(jButton3, "Skip");
        jButton3.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton3ActionPerformed(evt);
            }
        });

        jButton4.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/radio/lastfm/icons/control_stop.png"))); // NOI18N
        org.openide.awt.Mnemonics.setLocalizedText(jButton4, "Stop");
        jButton4.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton4ActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jLabel3, "Music related to");

        jTPMetaData.setContentType("text/html");
        jTPMetaData.setEditable(false);
        jTPMetaData.setDoubleBuffered(true);
        jScrollPane2.setViewportView(jTPMetaData);

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
                    .add(org.jdesktop.layout.GroupLayout.LEADING, jScrollPane2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 306, Short.MAX_VALUE)
                    .add(org.jdesktop.layout.GroupLayout.LEADING, jPanel1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .add(org.jdesktop.layout.GroupLayout.LEADING, layout.createSequentialGroup()
                        .add(jLabel3)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jComboBox1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jTSearchValue, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 146, Short.MAX_VALUE))
                    .add(org.jdesktop.layout.GroupLayout.LEADING, layout.createSequentialGroup()
                        .add(jButton2)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jButton3)
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jButton4)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(layout.createSequentialGroup()
                .addContainerGap()
                .add(jPanel1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 72, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(jComboBox1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(jTSearchValue, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(jLabel3))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(jButton2)
                    .add(jButton3)
                    .add(jButton4))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jScrollPane2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 236, Short.MAX_VALUE)
                .addContainerGap())
        );
    }// </editor-fold>//GEN-END:initComponents

private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
    PlayerService.stop(instance);
    StationUrl station = PlayerService.getNewStationUrl(
                jComboBox1.getSelectedItem().toString(),
                jTSearchValue.getText().trim());
    PlayerService.setStationUrl(station);
    PlayerService.play(instance); 
}//GEN-LAST:event_jButton2ActionPerformed

private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed
    PlayerService.stop(instance);
}//GEN-LAST:event_jButton4ActionPerformed

private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed
    //PlayerService.skip(); // Does not work - why?
    PlayerService.stop(instance);
    PlayerService.play(instance);
}//GEN-LAST:event_jButton3ActionPerformed

private void jBSignInActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jBSignInActionPerformed
    final String username = jTUserName.getText();
    final String password = jPUserPassword.getText();
    updateUserCredentials(username, password);
    
    class SignIn extends SwingWorker<Boolean, Object> {

        @Override
        protected Boolean doInBackground() throws Exception {
            jBSignIn.setEnabled(false);
            return PlayerService.signIn(username, password, instance);
        }
        
        @Override
        protected void done() {
            try {
                Boolean signed = get();
                jLSignStatus.setIcon((signed == true ? accept : reject));
            } catch (InterruptedException ex) {
                Exceptions.printStackTrace(ex);
            } catch (ExecutionException ex) {
                Exceptions.printStackTrace(ex);
            } finally {
                jBSignIn.setEnabled(true);
            }
        }
        
    }
    new SignIn().execute();
}//GEN-LAST:event_jBSignInActionPerformed

/**
 * Keeps just user name for now. It is possible to keep
 * password in MD5 but I don't want to do it now.
 * @param username
 * @param password not used now, can be null
 */
private void updateUserCredentials(String username, String password) {
    if (jCRememberMe.isSelected()) {
        NbPreferences.forModule(LastFMPanelTopComponent.class).put("rememberme", "yes");
        NbPreferences.forModule(LastFMPanelTopComponent.class).put("username", username);
    } else {
        NbPreferences.forModule(LastFMPanelTopComponent.class).remove("rememberme");
        NbPreferences.forModule(LastFMPanelTopComponent.class).remove("username");
    }
}

private void loadUserPreferencies() {
    if ("yes".equals(NbPreferences.forModule(LastFMPanelTopComponent.class).get("rememberme", "false"))) {
        jTUserName.setText(NbPreferences.forModule(LastFMPanelTopComponent.class).get("username", ""));
        jCRememberMe.setSelected(true);
    } else {
        jCRememberMe.setSelected(false);
    }
}

private void jCRememberMeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCRememberMeActionPerformed
    updateUserCredentials(jTUserName.getText(), jPUserPassword.getText());
}//GEN-LAST:event_jCRememberMeActionPerformed

private void jTSearchValueActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jTSearchValueActionPerformed
    jButton2ActionPerformed(evt);
}//GEN-LAST:event_jTSearchValueActionPerformed


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jBSignIn;
    private javax.swing.JButton jButton2;
    private javax.swing.JButton jButton3;
    private javax.swing.JButton jButton4;
    private javax.swing.JCheckBox jCRememberMe;
    private javax.swing.JComboBox jComboBox1;
    private javax.swing.JLabel jLSignStatus;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JPasswordField jPUserPassword;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JTextPane jTPMetaData;
    private javax.swing.JTextField jTSearchValue;
    private javax.swing.JTextField jTUserName;
    // End of variables declaration//GEN-END:variables
    /**
     * Gets default instance. Do not use directly: reserved for *.settings files only,
     * i.e. deserialization routines; otherwise you could get a non-deserialized instance.
     * To obtain the singleton instance, use {@link findInstance}.
     */
    public static synchronized LastFMPanelTopComponent getDefault() {
        if (instance == null) {
            instance = new LastFMPanelTopComponent();
        }
        return instance;
    }

    /**
     * Obtain the LastFMPanelTopComponent instance. Never call {@link #getDefault} directly!
     */
    public static synchronized LastFMPanelTopComponent findInstance() {
        TopComponent win = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
        if (win == null) {
            Logger.getLogger(LastFMPanelTopComponent.class.getName()).warning(
                    "Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system.");
            return getDefault();
        }
        if (win instanceof LastFMPanelTopComponent) {
            return (LastFMPanelTopComponent) win;
        }
        Logger.getLogger(LastFMPanelTopComponent.class.getName()).warning(
                "There seem to be multiple components with the '" + PREFERRED_ID +
                "' ID. That is a potential source of errors and unexpected behavior.");
        return getDefault();
    }

    @Override
    public int getPersistenceType() {
        return TopComponent.PERSISTENCE_ALWAYS;
    }

    @Override
    public void componentOpened() {
        // TODO add custom code on component opening
    }

    @Override
    public void componentClosed() {
        // TODO add custom code on component closing
    }

    /** replaces this in object stream */
    @Override
    public Object writeReplace() {
        return new ResolvableHelper();
    }

    @Override
    protected String preferredID() {
        return PREFERRED_ID;
    }

    final static class ResolvableHelper implements Serializable {

        private static final long serialVersionUID = 1L;

        public Object readResolve() {
            return LastFMPanelTopComponent.getDefault();
        }
    }

    public void resultChanged(LookupEvent arg0) {
        updateMetaData(result);
    }

    private void updateMetaData(Result<MetaData> result) {
        int cnt = result.allInstances().size();
        //logger.log(Level.INFO, "Result instances = " + cnt);
        jTPMetaData.setText("");
        if (cnt > 0) {
            StringBuffer d = new StringBuffer();
            Collection<MetaData> metaDataCollection = (Collection<MetaData>) result.allInstances();
            
            MetaData md = metaDataCollection.iterator().next();
            System.out.println(md);
            String html = getHtmlForMetaData(md);
            jTPMetaData.setText(html);
            //logger.log(Level.INFO, html);
        }        
    }
    
    private String getHtmlForMetaData(MetaData md) {
        StringBuffer sb = new StringBuffer();
        sb.append("<html><head><style>" +
            "BODY{margin:5pt;font-family:sans-serif;font-size:12pt;background-color:#939393;color:white;} " +
            ".cover{padding:2pt;border-width:1px;border-style:solid;border-color:#cccccc;margin:0pt;} " +
            ".sec{color:#cccccc;} " +
            "a{text-decoration:none;color:white;} " +
            "a:hover{text-decoration:underline;color:white;} " +
            "b{color:black;} " +
            "p{margin-top:2pt;margin:5pt;} " +
            "</style></head><body>");
        if (md != null) {
            sb.append("<b>Artist:</b> <a href='"+md.getArtist().getArtistUrl()+"'>" + md.getArtist().getArtistName() + "<a/><br>");
            sb.append("<b>Song:</b> <a href='"+md.getTrack().getTrackUrl()+"'>" + md.getTrack().getTrackName() + "</a> <span class='sec'>("+
                        md.getTrack().getTrackDuration()+"&nbsp;sec)</span><br>");
            sb.append("<b>Album:</b> <a href='"+md.getAlbum().getAlbumPageUrl()+"'>" + md.getAlbum().getAlbumName() + "</a><br>");
            sb.append("<p><img class='cover' src='" + md.getAlbum().getCoverSmallUrl() + "'></p>");
        }
        sb.append("</body></html>");
        return sb.toString();
    }
}
